home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Audio / LPCView / Source / PCHPlot.m < prev    next >
Encoding:
Text File  |  1992-03-07  |  16.1 KB  |  784 lines

  1.  
  2. /* Generated by Interface Builder */
  3.  
  4. #import "PCHPlot.h"
  5. #import "LPCView.h"
  6. #import "Dispatcher.h"
  7. #import <appkit/Application.h>
  8. #import <appkit/Window.h>
  9. #import <appkit/ScrollView.h>
  10. #import <appkit/Panel.h>
  11. #import <appkit/Cell.h>
  12. #import <appkit/SavePanel.h>
  13. #import <appkit/OpenPanel.h>
  14. #import <strings.h>
  15. #import <sys/types.h>
  16. #import <sys/stat.h>
  17. #import <sys/file.h>
  18. #import <libc.h>
  19. #import <dpsclient/dpsclient.h>
  20. #import <math.h>
  21. #import <sound/sound.h>
  22. #import <stdio.h>
  23. #import <streams/streams.h>
  24. #import <dpsclient/wraps.h>
  25. #import "PWfft.h"
  26.  
  27. #define PITCH    0
  28. #define AMP    1
  29. #define SIZE    2
  30.  
  31. #define MAXZOOM    4
  32. #define MINZOOM    -3
  33.  
  34. @implementation PCHPlot
  35.  
  36. extern int errno;
  37.  
  38. + new:(const char *)title
  39. {
  40.     NXRect aRect;
  41.     
  42.     self = [super new];
  43.     // Set up memory
  44.     PSops = NULL;
  45.     PSAdata = NULL;
  46.     PSPdata = NULL;
  47.     [NXApp loadNibSection:"pchplot.nib" owner:self];
  48.     [myView setDelegate:self];
  49.     [myView setParent:self];
  50.     [myPlot setDelegate:self];
  51.     numBytes = 0;    
  52.     zoomfactor = 1;
  53.     curzoom = 0;
  54.     PWinit();
  55.     [self setDirty:NO];
  56.  
  57.     // Set up view dependencies
  58.     
  59.     [myScroll getDocVisibleRect:&aRect];
  60.     [myView setFrame:&aRect];
  61.     [myScroll setDocView:myView];
  62.     [myScroll setHorizScrollerRequired:YES];
  63.     [myScroll setVertScrollerRequired:NO];
  64.     
  65.     // Set the title of the window - filename or Untitled 
  66.     strcpy(filename, title);
  67.     if (!strcmp(filename, "Untitled")) {
  68.         hasData = NO;
  69.         [myPlot setTitleAsFilename:"Untitled"];
  70.         [self show:self];
  71.         return self;
  72.     }
  73.     else {
  74.         [myPlot setTitleAsFilename:filename];
  75.         if ([self setPCHfile:filename] < 0) {
  76.             hasData = NO;
  77.             [myPlot setTitleAsFilename:"Untitled"];
  78.             [self show:self];
  79.             return self;
  80.         }
  81.         hasData = YES;
  82.     }
  83.     [self show:self];
  84.     return self;
  85. }
  86.  
  87. - (int) setPCHfile:(const char *)file
  88. {
  89.     struct stat st;
  90.     
  91.     openfd = open(file, O_RDWR, 0);
  92.     if (openfd < 0) {
  93.         [self error:"Could not open input file : ":errno];
  94.         return -1;
  95.     }
  96.     fstat(openfd, &st);
  97.     frames = (st.st_size/(SIZE*(sizeof(float))))-1;
  98.     numBytes = st.st_size;
  99.     [numFrames setIntValue:frames];
  100.     pchData = (float *)malloc(st.st_size);
  101.     if (read(openfd, pchData, st.st_size) < 0) {
  102.         [self error:"Error reading input file : ":errno];
  103.         return -1;
  104.     }
  105.     return 0;    
  106. }
  107.  
  108. - drawData
  109. {
  110.     int     i, winwidth;
  111.     float     yscalea, yscalep, half, zoomsize;
  112.     NXRect     viewBounds, visBounds, scrollBounds;
  113.     NXSize    scrollSize;
  114.     float    amax, amin;    // amplitude boundaries
  115.     float    ptchmax, ptchmin;    // pitch boundaries 
  116.     // DPSUserPath stuff
  117.     float    bbox[4];    
  118.     float    *t, *a, *ptch;    // temporary variables
  119.     char    *o;
  120.     
  121.     //  Only draw plot if such information exists
  122.     if (!hasData) 
  123.         return self;
  124.         
  125.     amax = MINFLOAT;  ptchmax = MINFLOAT;
  126.     amin = MAXFLOAT;  ptchmin = MAXFLOAT;
  127.     
  128.     t = pchData;
  129.     for (i = 0; i < frames; i++) {
  130.         if (t[AMP] > amax) 
  131.             amax = t[AMP];
  132.         if (t[AMP] < amin)
  133.             amin = t[AMP];
  134.         if (t[PITCH] > ptchmax)
  135.             ptchmax = t[PITCH];
  136.         if (t[PITCH] < ptchmin)
  137.             ptchmin = t[PITCH];
  138.         t += SIZE;
  139.     }
  140.     if (amax == amin) {
  141.         if (amax > 0)
  142.             amin = 0;
  143.         else if (amax == 0)
  144.             amin = -1;
  145.         else 
  146.             amin = 2*amax;
  147.     }
  148.     if (ptchmax == ptchmin) {
  149.         if (ptchmax > 0)
  150.             ptchmin = 0;
  151.         else if (ptchmax == 0)
  152.             ptchmin = -1;
  153.         else
  154.             ptchmin = 2*ptchmax;
  155.     }
  156.  
  157.     [myView getBounds:&viewBounds];    
  158.     [myView getVisibleRect:&visBounds];
  159.     [myScroll getContentSize:&scrollSize];
  160.     
  161.     [myScroll getDocVisibleRect:&scrollBounds];
  162.     [myView getVisibleRect:&visBounds];
  163.     scrollBounds.size.width *= zoomfactor;
  164.     /* Bug in DSPUserpath makes drawing at > 4096 bomb */
  165.     while (scrollBounds.size.width > 4096.0) {
  166.         // Simulate a zoom out
  167.         scrollBounds.size.width /= zoomfactor;
  168.         --curzoom;
  169.         zoomfactor /= 1.5;
  170.         scrollBounds.size.width *= zoomfactor;
  171.         printf("too big -- new width = %f\n", scrollBounds.size.width);
  172.     }
  173.     [myView sizeTo:scrollBounds.size.width :scrollBounds.size.height];
  174.     [myView getBounds:&viewBounds];
  175.     [myView lockFocus];
  176.     PSsetgray(NX_WHITE);
  177.     NXRectFill(&viewBounds);
  178.     [myView unlockFocus];
  179.     winwidth = scrollBounds.size.width;
  180.     zoomsize = frames;
  181.     step = 1.0*zoomsize/winwidth;
  182.     half = 1.0*(visBounds.size.height)/2.0;
  183.     yscalea = 1.0*(half - 25.0)/(1.0*(amax - amin));
  184.     yscalep = 1.0*(half - 25.0)/(1.0*(ptchmax - ptchmin));
  185.     if (!PSAdata) PSAdata = (float *)malloc(4*winwidth*sizeof(float));
  186.     if (!PSPdata) PSPdata = (float *)malloc(4*winwidth*sizeof(float));
  187.     if (!PSops) PSops = (char *)malloc(2*winwidth*sizeof(char));
  188.     
  189.     t = pchData;
  190.     for (i = 0, a = PSAdata, ptch = PSPdata, o = PSops; 
  191.         i < winwidth; i++) {
  192.         *a++ = i;
  193.         *a++ = 20.0;
  194.         *a++ = 0.0;
  195.         *a++ = ((t[AMP] - amin)*yscalea);
  196.  
  197.         *ptch++ = i;    
  198.         *ptch++ = half+20.0;
  199.         *ptch++ = 0.0;
  200.         *ptch++ = ((t[PITCH] - ptchmin)*yscalep);
  201.         *o++ = dps_moveto;
  202.         *o++ = dps_rlineto;
  203.  
  204.         t = (pchData) + (SIZE * (int)(i * step));
  205.     }
  206.     bbox[0] = 0.0;
  207.     bbox[1] = 0.0;
  208.     bbox[2] = winwidth;
  209.     bbox[3] = ((amax - amin)*yscalea) + half;
  210.     [myView drawPlot:PSAdata:PSops:bbox:winwidth:NX_BLACK];
  211.     bbox[1] = half;
  212.     bbox[3] = visBounds.size.height;
  213.     [myView drawPlot:PSPdata:PSops:bbox:winwidth:NX_BLACK];
  214.     PWdrawruler(0, frames,(20*frames/winwidth), (20*frames/winwidth)/step);
  215.     if (PSAdata) free(PSAdata);
  216.     if (PSPdata) free(PSPdata);
  217.     if (PSops) free(PSops);
  218.     PSAdata = NULL;
  219.     PSPdata = NULL;
  220.     PSops = NULL;
  221.     return self;
  222. }
  223.  
  224. - show:sender
  225. {
  226.     [myPlot makeKeyAndOrderFront:self];
  227.     [myView display];    
  228.     return self;
  229. }
  230.  
  231. - zoomIn:sender
  232. {
  233.     NXRect foo;
  234.     
  235.     if (curzoom >= MAXZOOM) 
  236.         return self;
  237.     [myView getBounds:&foo];
  238.     if ((foo.size.width * 1.5) > 4096.0)
  239.         return self;
  240.     ++curzoom;
  241.     zoomfactor *= 1.5;
  242.     [myView display];    
  243.     return self;
  244. }
  245.  
  246. - zoomOut:sender
  247. {
  248.     if (curzoom <= MINZOOM)
  249.         return self;
  250.     --curzoom;
  251.     zoomfactor /= 1.5;
  252.     [myView display];
  253.     return self;
  254. }
  255.  
  256. - print:sender
  257. {
  258.     [myView printPSCode:sender];
  259.     return self;    
  260. }
  261.  
  262. - updateCursor:sender
  263. {
  264.     float    amp, pitch, *t;
  265.     int    cursor, width, frame1, frame2, temp;
  266.     
  267.     if (!hasData) 
  268.         return self;
  269.     cursor = [myView getcurPos];
  270.     width = [myView getWidth];
  271.  
  272.     frame1 = (int)(cursor * step);
  273.     frame2 = (int)((cursor + width) * step);
  274.     if (frame1 > frame2) {
  275.         temp = frame1;
  276.         frame1 = frame2;
  277.         frame2 = temp;
  278.     }
  279.     [startFrame setIntValue:frame1];
  280.     [endFrame setIntValue:frame2];
  281.     t = pchData + (SIZE * (int)((cursor + width) * step));
  282.     amp = t[AMP];
  283.     pitch = t[PITCH];
  284.     [ampFrame setFloatValue:amp];
  285.     [pitchFrame setFloatValue:pitch];
  286.     [playFrame1 setIntValue:(frame1 + 1)];
  287.     [playFrame2 setIntValue:(frame2 + 1)];
  288.     return self;
  289. }
  290.  
  291. - selectAll:sender
  292. {
  293.     [myView changeCurs:0:(frames/step)];
  294.     [playFrame1 setIntValue:1];
  295.     [playFrame2 setIntValue:(frames+1)];
  296.     return self;
  297. }
  298.  
  299. - cursorSel:sender
  300. {
  301.     [myView setCursorType:SELECTION];
  302.     return self;
  303. }
  304.  
  305. - cursorHair:sender
  306. {
  307.     [myView setCursorType:HAIRLINE];
  308.     return self;
  309. }
  310.  
  311. - changeCursor:sender
  312. {
  313.     int    frame1, frame2, temp;
  314.     float    loc, loc2, width;
  315.     
  316.     frame1 = [startFrame intValue];
  317.     frame2 = [endFrame intValue];
  318.  
  319.     frame1 = (frame1 < 0) ? 0 : frame1;
  320.     frame1 = (frame1 > frames) ? frames : frame1;
  321.     frame2 = (frame2 < 0) ? 0 : frame2;
  322.     frame2 = (frame2 > frames) ? frames : frame2;
  323.  
  324.     if (frame1 > frame2) {
  325.         temp = frame1;
  326.         frame1 = frame2;
  327.         frame2 = temp;
  328.     }
  329.         
  330.     loc = frame1 / step;
  331.     loc2 = frame2 / step;
  332.     width = loc2 - loc;
  333.     [myView changeCurs:loc:width];
  334.     return self;
  335. }
  336.  
  337. - changeAmp:sender
  338. {
  339.     float    *t, val;
  340.     int    cursor, width, i, frame1, frame2, temp;
  341.     
  342.     cursor = [myView getcurPos];
  343.     width = [myView getWidth];
  344.     frame1 = (int)cursor * step;
  345.     frame2 = (int)(cursor + width) * step;
  346.     if (frame1 > frame2) {
  347.         temp = frame1;
  348.         frame1 = frame2;
  349.         frame2 = temp;
  350.     }
  351.     t = pchData + (SIZE * frame1);
  352.     val = [ampFrame floatValue];
  353.     [self saveundo:(frame2 - frame1 + 1)*SIZE*sizeof(float):t];
  354.     for (i = frame1; i <= frame2; i++) {
  355.         t[AMP] = val;
  356.         t = (pchData) + (SIZE * (i + 1));
  357.     }
  358.     [myView display];
  359.     [self setDirty:YES];
  360.     return self;        
  361. }
  362.  
  363. - changePitch:sender
  364. {
  365.     float    *t, val;
  366.     int    cursor, width, i, frame1, frame2, temp;
  367.     
  368.     cursor = [myView getcurPos];
  369.     width = [myView getWidth];
  370.     frame1 = (int)cursor * step;
  371.     frame2 = (int)(cursor + width) * step;
  372.     if (frame1 > frame2) {
  373.         temp = frame1;
  374.         frame1 = frame2;
  375.         frame2 = temp;
  376.     }
  377.     t = pchData + (SIZE * frame1);
  378.     val = [pitchFrame floatValue];
  379.     [self saveundo:(frame2 - frame1 + 1)*SIZE*sizeof(float):t];
  380.     for (i = frame1; i <= frame2; i++) {
  381.         t[PITCH] = val;
  382.         t = (pchData) + (SIZE * (i + 1));
  383.     }
  384.     [myView display];
  385.     [self setDirty:YES];
  386.     return self;
  387. }
  388.  
  389. - doCopy:(NXStream *)selection
  390. {
  391.     float    *t;
  392.     int    cursor, width, frame1, frame2, temp, bytes;
  393.     
  394.     if (!hasData) {
  395.         return self;
  396.     }
  397.     cursor = [myView getcurPos];
  398.     width = [myView getWidth];
  399.     frame1 = (int)cursor * step;
  400.     frame2 = (int)(cursor + width) * step;
  401.     if (frame1 > frame2) {
  402.         temp = frame1;
  403.         frame1 = frame2;
  404.         frame2 = temp;
  405.     }
  406.     t = pchData + (SIZE * frame1);
  407.     bytes =  (frame2 - frame1)*SIZE*sizeof(float);
  408.     NXWrite(selection, t, bytes);
  409.     return self;
  410.     
  411. }
  412.  
  413. - doCut:(NXStream *)selection
  414. {
  415.     float    *t, *v;
  416.     int    cursor, width, i, frame1, frame2, temp, bytes;
  417.  
  418.     if (!hasData) {
  419.         return self;
  420.     }
  421.     cursor = [myView getcurPos];
  422.     width = [myView getWidth];
  423.     frame1 = (int)cursor * step;
  424.     frame2 = (int)(cursor + width) * step;
  425.     if (frame1 > frame2) {
  426.         temp = frame1;
  427.         frame1 = frame2;
  428.         frame2 = temp;
  429.     }
  430.     t = pchData + (SIZE * frame1);
  431.     bytes =  (frame2 - frame1)*SIZE*sizeof(float);
  432.     NXWrite(selection, t, bytes);
  433.     numBytes -= bytes;
  434.     frames = (numBytes/(SIZE*sizeof(float)))-1;
  435.     [numFrames setIntValue:frames];
  436.     v = t + (bytes / sizeof(float));
  437.     for (i = 0; i < (bytes / sizeof(float)); i++) {
  438.         *t++ = *v++;
  439.     }
  440.     realloc(pchData, numBytes);
  441.     [myView display];
  442.     [self setDirty:YES];
  443.     return self;
  444.     
  445. }
  446.  
  447. - doInsert:(float *)data :(int) bytes
  448. {
  449.     int    cursor, frame1, size, i;
  450.     float    *t, *v;
  451.     
  452.     if (!hasData) {
  453.         hasData = 1;
  454.         pchData = (float *)malloc(bytes);
  455.         numBytes = bytes;
  456.         frames = (numBytes/(SIZE*sizeof(float))) - 1;
  457.         [numFrames setIntValue:frames];
  458.         t = pchData;
  459.         for (i = 0; i < (bytes / sizeof(float)); i++) {
  460.             *t++ = *data++;
  461.         }
  462.         [myView display];
  463.         return self;
  464.     }
  465.     numBytes += bytes;
  466.     frames = (numBytes/(SIZE*sizeof(float)))-1;
  467.     [numFrames setIntValue:frames];
  468.     realloc(pchData, numBytes);
  469.     cursor = [myView getcurPos];
  470.     frame1 = (int)cursor * step;
  471.     size = (numBytes - bytes) - (frame1 * SIZE * sizeof(float));
  472.  
  473.     v = pchData + (numBytes / sizeof(float)) - 1;
  474.     t = pchData + ((numBytes - bytes) / sizeof(float)) - 1;
  475.     for (i = 0; i < (size / sizeof(float)); i++) {
  476.         *v-- = *t--;
  477.     }
  478.     t = pchData + (SIZE * frame1);
  479.     for (i = 0; i < (bytes / sizeof(float)); i++) {
  480.         *t++ = *data++;
  481.     }
  482.     [myView display];
  483.     [self setDirty:YES];
  484.     return self;
  485. }
  486.  
  487. - saveundo:(int)size: (float *)data
  488. {
  489.     int    i;
  490.     float    *t, *s;
  491.     
  492.     if (undoData) 
  493.         free(undoData);
  494.     undoData = (float *)malloc(size);
  495.     undoLoc = data;
  496.     undoSize = size;
  497.     t = undoData;
  498.     s = data;
  499.     for (i = 0; i < (undoSize / 4); i++) {
  500.         *t++ = *s++;
  501.     }
  502.     return self;
  503. }
  504.  
  505. - undo:sender
  506. {
  507.     float    *t, *s;
  508.     int    i;
  509.     
  510.     t = undoLoc;
  511.     s = undoData;
  512.     for (i = 0; i < (undoSize / 4); i++) {
  513.         *t++ = *s++;
  514.     }
  515.     [myView display];
  516.     return self;
  517. }
  518.  
  519. - multiplyAmp:sender
  520. {
  521.     float    *t, val;
  522.     int    cursor, width, i, frame1, frame2, temp;
  523.     
  524.     cursor = [myView getcurPos];
  525.     width = [myView getWidth];
  526.     frame1 = (int)cursor * step;
  527.     frame2 = (int)(cursor + width) * step;
  528.     if (frame1 > frame2) {
  529.         temp = frame1;
  530.         frame1 = frame2;
  531.         frame2 = temp;
  532.     }
  533.     val = [mulAmpFrame floatValue];
  534.     t = pchData + (SIZE * frame1);
  535.     [self saveundo:(frame2 - frame1 + 1)*SIZE*sizeof(float):t];
  536.     for (i = frame1; i <= frame2; i++) {
  537.         t[AMP] *= val;
  538.         t = (pchData) + (SIZE * (i + 1));
  539.     }
  540.     [myView display];
  541.     [self setDirty:YES];
  542.     return self;
  543. }
  544.  
  545. - multiplyPitch:sender
  546. {
  547.     float    *t, val, top, bottom;
  548.     int    cursor, width, i, frame1, frame2, temp;
  549.     
  550.     cursor = [myView getcurPos];
  551.     width = [myView getWidth];
  552.     t = pchData + (SIZE * (int)(cursor * step));
  553.     frame1 = (int)cursor * step;
  554.     frame2 = (int)(cursor + width) * step;
  555.     if (frame1 > frame2) {
  556.         temp = frame1;
  557.         frame1 = frame2;
  558.         frame2 = temp;
  559.     }
  560.     val = [mulPitchFrame floatValue];
  561.     top = [topPitchFrame floatValue];
  562.     bottom = [botPitchFrame floatValue];
  563.     t = pchData + (SIZE * frame1);
  564.     [self saveundo:(frame2 - frame1 + 1)*SIZE*sizeof(float):t];    
  565.     for (i = frame1; i <= frame2; i++) {
  566.         if (t[PITCH] <= top && t[PITCH] >= bottom)
  567.             t[PITCH] *= val;
  568.         t = (pchData) + (SIZE * (i + 1));
  569.     }
  570.     [myView display];
  571.     [self setDirty:YES];
  572.     return self;
  573. }
  574.  
  575. - interpolate:sender
  576. {
  577.     float    *t, *v, first, last, scale;
  578.     int    cursor, width, i, frame1, frame2, temp, j;
  579.     
  580.     cursor = [myView getcurPos];
  581.     width = [myView getWidth];
  582.     frame1 = ((int)(cursor * step)) - 1;
  583.     frame2 = ((int)((cursor + width) * step)) + 1;
  584.     if (frame1 > frame2) {
  585.         temp = frame1;
  586.         frame1 = frame2;
  587.         frame2 = temp;
  588.     }
  589.     t = pchData + (SIZE * frame1);
  590.     v = pchData + (SIZE * frame2);
  591.     first = t[PITCH];
  592.     last = v[PITCH]; 
  593.     t = pchData + (SIZE * frame1);
  594.     scale = (last - first) / (frame2 - frame1);
  595.     [self saveundo:(frame2 - frame1 + 1)*SIZE*sizeof(float):t];    
  596.     for (i = frame1, j = 0; i <= frame2; i++) {
  597.         t[PITCH] = first + (j * scale);
  598.         t = (pchData) + (SIZE * (i + 1));
  599.         ++j;
  600.     }
  601.     [myView display];
  602.     [self setDirty:YES];
  603.     return self;
  604. }
  605.  
  606. - save:sender
  607. {
  608.     id    sPanel;
  609.     
  610.     sPanel = [SavePanel new];
  611.     if (!strcmp("Untitled", filename)) {
  612.         if ([sPanel runModal]) {
  613.             strcpy(filename, [sPanel filename]);
  614.             [myPlot setTitleAsFilename:filename];
  615.             openfd = open(filename, O_RDWR|O_CREAT, 0644);
  616.             if (openfd < 0) {
  617.                 [self error:"Error opening input file : ":errno];
  618.                 return self;
  619.             }
  620.             if (write(openfd, (char *)pchData, numBytes) < 0) {
  621.                 [self error:"Error writing file : ":errno];
  622.                 return self;
  623.             };
  624.             [self setDirty:NO];
  625.             return self;
  626.         }
  627.     } 
  628.     else {
  629.         lseek(openfd, 0, 0);  // start of file
  630.         if (write(openfd,(char *)pchData,numBytes) < 0) {
  631.             [self error:"Error writing file : ":errno];
  632.             return self;
  633.         }
  634.         [self setDirty:NO];
  635.         return self;
  636.     }
  637.     return self;
  638. }
  639.  
  640. - saveAs:sender
  641. {
  642.     id    sPanel;
  643.     char    dir[1024],  *file;
  644.     
  645.     sPanel = [SavePanel new];
  646.     file = rindex(filename, '/') + 1;
  647.     strncpy(dir, filename, (int)(file - filename));
  648.     if ([sPanel runModalForDirectory:dir file:file]) {
  649.         if (strcmp(filename, [sPanel filename])) {
  650.             strcpy(filename, [sPanel filename]);
  651.             close(openfd);
  652.             openfd = open(filename, O_RDWR|O_CREAT, 0644);
  653.             if (openfd < 0) {
  654.                 [self error:"Error saving file : ":errno];
  655.                 return self;
  656.             }
  657.             if (write(openfd, (char *)pchData, numBytes) < 0) {
  658.                 [self error:"Error saving file : ":errno];
  659.                 return self;
  660.             }
  661.             [myPlot setTitleAsFilename:filename];
  662.             [self setDirty:NO];
  663.             return self;
  664.         }
  665.         else {
  666.             lseek(openfd, 0, 0);
  667.             if (write(openfd, (char *)pchData, numBytes) < 0) {
  668.                 [self error:"Error saving file : ":errno];
  669.                 return self;
  670.             }
  671.             [self setDirty:NO];
  672.             return self;
  673.         }
  674.     }
  675.     return self;
  676. }
  677.  
  678. - setOutputFile:sender
  679. {
  680.     id    openP;
  681.     char    *types[2];
  682.     
  683.     types[0] = "snd";
  684.     types[1] = 0;
  685.     openP = [OpenPanel new];
  686.     [openP runModalForTypes:types];
  687.     if ([openP filenames]) {
  688.         strcpy(soundFilename, [openP filename]);
  689.         [soundFileFrame setStringValue:soundFilename];
  690.     }    
  691.     return self;
  692. }
  693.  
  694. - playOutputSound:sender
  695. {
  696.      int    err;
  697.     SNDSoundStruct *s;
  698.     
  699.     strcpy(soundFilename, [soundFileFrame stringValue]);
  700.     if (!strlen(soundFilename)) 
  701.         return self;
  702.     err = SNDReadSoundfile(soundFilename, &s);
  703.     if (!err) {
  704.         err = SNDStartPlaying(s, 1, 5, 0, 0,(SNDNotificationFun)SNDFree);
  705.         if (!err)
  706.             SNDWait(1);
  707.         else 
  708.             [self error:"Error playing soundfile":0];
  709.     }
  710.     else {
  711.         [self error:"Error reading soundfile":0];
  712.     }
  713.     return self;
  714. }
  715.  
  716. - play:sender
  717. {
  718.     return self;
  719. }
  720.  
  721. - doPitchAnal:sender
  722. {
  723.     char    cmd[1024];
  724.     
  725.     strcpy(soundFilename, [soundFileFrame stringValue]);
  726.     if (!strlen(soundFilename)) {
  727.         strcpy(soundFilename, "/tmp/anal.snd");
  728.         [soundFileFrame setStringValue:soundFilename];
  729.     }
  730.     sprintf(cmd, "%s %s %s %d %d", [NXApp playpitch], filename, soundFilename, [playFrame1 intValue], [playFrame2 intValue]);
  731.     system(cmd);
  732.     return self;
  733. }
  734.  
  735. - drawPlot:sender
  736. {
  737.     return self;
  738. }
  739.  
  740. - plot
  741. {
  742.     return myPlot;
  743. }
  744.  
  745. - error:(char *)msg:(int)num
  746. {
  747.     if (num)
  748.         NXRunAlertPanel("Alert", "%s : %s", "OK", NULL, NULL, msg, strerror(num));
  749.     else
  750.         NXRunAlertPanel("Alert", "%s", "OK", NULL, NULL, msg);
  751.     return self;
  752. }
  753.  
  754. - windowWillClose:sender
  755. {
  756.     if ([myPlot isDocEdited]) {
  757.         switch (NXRunAlertPanel("Alert", "Plot has been edited.  Do you want to save?", "Yes", "No", "Cancel")) {
  758.             case NX_ALERTDEFAULT :
  759.                 [self saveAs:self];
  760.                 break;
  761.             case NX_ALERTOTHER :
  762.                 return nil;
  763.                 break;
  764.             default :
  765.                 break;
  766.         }
  767.     }
  768.     return self;
  769. }
  770.  
  771. - setDirty:(BOOL)val
  772. {
  773.     [myPlot setDocEdited:val];
  774.     dirty = val;
  775.     return self;
  776. }
  777.  
  778. - (BOOL)isDirty
  779. {
  780.     return dirty;
  781. }
  782.  
  783. @end
  784.